/******************************************************************************
 * FILE: mpi_latency.c
 * DESCRIPTION:
 *   MPI Latency Timing Program - C Version
 *   In this example code, a MPI communication timing test is performed.
 *   MPI task 0 will send "reps" number of 1 byte messages to MPI task 1,
 *   waiting for a reply between each rep. Before and after timings are made
 *   for each rep and an average calculated when completed.
 * AUTHOR: Blaise Barney
 * LAST REVISED: 04/13/05
 ******************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <time.h>

#include <mpi/mpi.h>
#define NUMBER_REPS 1000

int main(int argc, char *argv[]) {
    int reps,         /* number of samples per test */
        tag,          /* MPI message tag parameter */
        numtasks,     /* number of MPI tasks */
        rank,         /* my MPI task number */
        dest, source, /* send/receive task designators */
        avgT,         /* average time per rep in microseconds */
        rc,           /* return code */
        n;
    double T1, T2,     /* start/end times per rep */
        sumT,          /* sum of all reps times */
        deltaT;        /* time for one rep */
    char msg;          /* buffer containing 1 byte message */
    MPI_Status status; /* MPI receive routine parameter */

    MPI_Init(&argc, &argv);
    MPI_Comm_size(MPI_COMM_WORLD, &numtasks);
    MPI_Comm_rank(MPI_COMM_WORLD, &rank);
    if (rank == 0 && numtasks != 2) {
        printf("Number of tasks = %d\n", numtasks);
        printf("Only need 2 tasks - extra will be ignored...\n");
    }
    MPI_Barrier(MPI_COMM_WORLD);

    sumT = 0;
    msg = 'x';
    tag = 1;
    reps = NUMBER_REPS;

    if (rank == 0) {
        /* round-trip latency timing test */
        printf("task %d has started...\n", rank);
        printf("Beginning latency timing test. Number of reps = %d.\n", reps);
        printf("***************************************************\n");
        printf("Rep#       T1               T2            deltaT\n");
        dest = 1;
        source = 1;
        for (n = 1; n <= reps; n++) {
            T1 = MPI_Wtime(); /* start time */
            /* send message to worker - message tag set to 1.  */
            /* If return code indicates error quit */
            rc = MPI_Send(&msg, 1, MPI_BYTE, dest, tag, MPI_COMM_WORLD);
            if (rc != MPI_SUCCESS) {
                printf("Send error in task 0!\n");
                MPI_Abort(MPI_COMM_WORLD, rc);
                exit(1);
            }
            /* Now wait to receive the echo reply from the worker  */
            /* If return code indicates error quit */
            rc = MPI_Recv(&msg, 1, MPI_BYTE, source, tag, MPI_COMM_WORLD,
                          &status);
            if (rc != MPI_SUCCESS) {
                printf("Receive error in task 0!\n");
                MPI_Abort(MPI_COMM_WORLD, rc);
                exit(1);
            }
            T2 = MPI_Wtime(); /* end time */

            /* calculate round trip time and print */
            deltaT = T2 - T1;
            printf("%4d  %8.8f  %8.8f  %2.8f\n", n, T1, T2, deltaT);
            sumT += deltaT;
        }
        avgT = (sumT * 1000000) / reps;
        printf("***************************************************\n");
        printf("\n*** Avg round trip time = %d microseconds\n", avgT);
        printf("*** Avg one way latency = %d microseconds\n", avgT / 2);
    }

    else if (rank == 1) {
        printf("task %d has started...\n", rank);
        dest = 0;
        source = 0;
        for (n = 1; n <= reps; n++) {
            rc = MPI_Recv(&msg, 1, MPI_BYTE, source, tag, MPI_COMM_WORLD,
                          &status);
            if (rc != MPI_SUCCESS) {
                printf("Receive error in task 1!\n");
                MPI_Abort(MPI_COMM_WORLD, rc);
                exit(1);
            }
            rc = MPI_Send(&msg, 1, MPI_BYTE, dest, tag, MPI_COMM_WORLD);
            if (rc != MPI_SUCCESS) {
                printf("Send error in task 1!\n");
                MPI_Abort(MPI_COMM_WORLD, rc);
                exit(1);
            }
        }
    }

    MPI_Finalize();
    exit(0);
}
